package com.ty.rank;

import com.alibaba.fastjson.annotation.JSONField;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * @author: Troy.Chen(失足程序员, 15388152619)
 * @version: 2021-08-01 22:14
 **/
public class RankCollection<V extends RankBase> {


    @JSONField(serialize = false, deserialize = false)
    private final ReentrantLock lock = new ReentrantLock();

    /*是否需要排序*/
    private volatile boolean needSort = false;
    ConcurrentHashMap<Long, V> memberMap = new ConcurrentHashMap<>();
    @JSONField(serialize = false, deserialize = false)
    private ArrayList<V> sortList = new ArrayList<>();

    private Comparator<V> comparator = null;

    private Function<Long, V> createRankMember = null;

    public RankCollection() {
    }

    public RankCollection(Function<Long, V> createRankMember) {
        this.createRankMember = createRankMember;
    }

    public RankCollection(Comparator<V> comparator, Function<Long, V> createRankMember) {
        this.comparator = comparator;
        this.createRankMember = createRankMember;
    }

    public void add(V value) {
        if (!memberMap.containsKey(value.getId())) {
            sortList.add(value);
        }
        memberMap.put(value.getId(), value);
    }

    public boolean update(long id, UpdateType updateType, long value) {
        final V rank = memberMap.computeIfAbsent(id, l -> createRankMember.apply(l));
        switch (updateType) {
            case Replace:
                rank.setValue(value);
                break;
            case Min:
                if (rank.getValue() <= value) {
                    return false;
                }
                rank.setValue(value);
                break;
            case Max:
                if (rank.getValue() >= value) {
                    return false;
                }
                rank.setValue(value);
                break;
            case Sum:
                rank.setValue(value + rank.getValue());
                break;
        }
        rank.setTime(System.currentTimeMillis());
        needSort = true;
        return true;
    }

    public V member(long k) {
        return memberMap.get(k);
    }

    public V sortMember(long k) {
        sort();
        return memberMap.get(k);
    }

    public void sort() {
        lock.lock();
        try {
            if (needSort) {
                sortList.sort(comparator);
                int rank = 0;
                for (V v : sortList) {
                    rank++;
                    v.setRank(rank);
                }
            }
        } finally {
            lock.unlock();
        }
    }

    public void foreach(Consumer<V> action) {
        foreach(-1, action);
    }

    public void foreach(int end, Consumer<V> action) {
        lock.lock();
        try {
            sort();

            if (sortList.size() < end) {
                end = sortList.size();
            }

            for (int i = 0; i < end; i++) {
                action.accept(sortList.get(i));
            }
        } finally {
            lock.unlock();
        }
    }

    public ArrayList<V> getSortList() {
        sort();
        return sortList;
    }

    public Comparator<V> getComparator() {
        return comparator;
    }

    public RankCollection<V> setComparator(Comparator<V> comparator) {
        this.comparator = comparator;
        return this;
    }

    public Function<Long, V> getCreateRankMember() {
        return createRankMember;
    }

    public RankCollection<V> setCreateRankMember(Function<Long, V> createRankMember) {
        this.createRankMember = createRankMember;
        return this;
    }
}
